/**
 * Motion.java
 *
 */

 package gamepack;

import java.util.Vector;
import java.util.Enumeration;
import java.awt.Rectangle;
import java.awt.Polygon;


 public class Motion {

 	public static boolean canWarp(Sprite s, int dx,int dy,int newFrame) {
 	    if( s.blockers == null ) {
            return true;
        }
        Collidable c = Collision.createCollidable( s , dx , dy );

		for( int f=0; f< s.blockers.length; f++) {
			for( Enumeration e=s.blockers[f].elements(); e.hasMoreElements(); ) {
				Sprite b = (Sprite) e.nextElement();
                if( s != b && Collision.between( c, b) ) {                    
					//System.out.println(" Cannot warp " + dx + "," + dy + " newFrame = " + newFrame + "Details - ");
					//System.out.println( "s.hasSubShape() = " + s.hasSubShape() + " c.hasSubShape() = " + 
					//c.hasSubShape() + " b.hasSubShape() = " + b.hasSubShape() );
                    return false;
                }
			}
		}		
		return true;
 	}

	public static boolean canStep(Sprite s,int dx,int dy) {
		return canWarp(s,dx,dy,s.curFrame);
	}

	public static boolean canStep(Sprite s,int direction) {
		int dy = (direction & Direction.NORTH) != 0 ? -1 : 0;
			dy = (direction & Direction.SOUTH) != 0 ?  1 : dy;
		int dx = (direction & Direction.WEST)  != 0 ? -1 : 0;
			dx = (direction & Direction.EAST)  != 0 ?  1 : dx;
		return canWarp(s,dx,dy,s.curFrame);
	}

    public static boolean canTurn( Sprite s, int nextFrame, int directionToTurn) {
           // TODO: fix this.. simply but buggy implementation provided for now.
           // use curDirection, directionToTurn and curFrame in the calculation.
           return canWarp( s, s.x, s.y, nextFrame );
    }

    public static Collidable turn( Sprite s, int nextFrame, int directionToTurn) {
           // TODO: fix this pending..
           s.curFrame = nextFrame;
           return null;
    }


    public static MotionResult canMove(Sprite s,int distX,int distY) {
           if ( distX ==0 &&distY ==0 )
              return new MotionResult( false, false, 0,0 );
           // TODO: Add faster implementation which does it
           // with a much less number of collisions.
           // (1/max(distX,distY)) factor less.
           return canMove_slowImpl( s,distX,distY );
    }

    // Slower implementation of canMove
    private static MotionResult canMove_slowImpl(Sprite s,int distX,int distY) {

	int x1=0,y1=0,x2=distX,y2=distY;

	// Start Line algorithm [Assumes x1,y1,x2,y2] ----------------------
		int deltaX = Math.abs(x2-x1);
		int deltaY = Math.abs(y2-y1);
		int deltaMax = Math.max(deltaX,deltaY);
		int xIncr = ( x2-x1>0 ) ? 1 : (x2-x1<0 ? -1:0 );
		int yIncr = ( y2-y1>0 ) ? 1 : (y2-y1<0 ? -1:0 );
		int cx = x1, cy = y1;
		double slope = (deltaX!=0)? ( (double)(y2-y1)/(x2-x1)):0;

		for(int i=0;i<deltaMax;i++) {

			int nextX = cx,nextY = cy;
			if( deltaX > deltaY ) {
				nextX+=xIncr;
				nextY = (int) (((nextX-x1)*slope + y1) + 0.5);
			}else {
				nextY+=yIncr;
				if( deltaX!=0 ) {
					nextX = (int) (((nextY-y1)/slope + x1) + 0.5);
				}
			}
			int dx = nextX-cx;	cx+=dx;
			int dy = nextY-cy;	cy+=dy;

			// Do your stuff. Use cx,cy,dx,dy --------------------------
			if( !canWarp( s, cx,cy,s.curFrame) ) {
			    MotionResult mr = new MotionResult( true, false, cx-dx , cy-dy );
			    /*System.out.println("cannotWarp cx(" + cx + ") cy(" + cy + ") dx(" + dx + ") dy(" + dy + ") " +
                  "nextX(" + nextX + ") nextY(" + nextY + ") distX(" + distX + ") distY(" + distY +
                  + deltaMax + "(" + i + ")" );*/
			    if( i == 0 ) {
                    mr.canMove = false;
                    mr.canMoveMax = false;
                } else {
                  //System.out.println("partially blocked ----");
                }
                return mr;
			}
		}
	    // End Line algorithm ----------------------------------------------
		return new MotionResult( true, true, distX, distY  );
    }

    public static MotionResult canMoveTowards(Sprite s, int direction,int distance) {
		int dx=0,dy=0;
		switch(direction) {
			case Direction.NORTH:			dy = - distance;	break;
			case Direction.SOUTH:			dy =   distance;	break;
			case Direction.WEST:			dx = - distance;	break;
			case Direction.EAST:			dx =   distance;	break;

			case Direction.NORTHEAST:		dx =   distance;	dy = - distance;	break;
			case Direction.NORTHWEST:		dx = - distance;	dy = - distance;	break;
			case Direction.SOUTHEAST:		dx =   distance;	dy =   distance;	break;
			case Direction.SOUTHWEST:		dx = - distance;	dy =   distance;	break;
		}
		return canMove(s,dx,dy);
	}
	// ----------- Move methods --------------
    // moves the sprite s by dx,dy and returns a collidable
    // object which may be tested for collision with other sprites
    // of interest.
	public static Collidable move(Sprite s,int dx,int dy) {
		   s.translate( dx,dy );
		   return Collision.createCollidable( s );
		   
/*	       Rectangle r = s.getBounds();
	       r.width += Math.abs(dx);
	       r.height += Math.abs(dy);
           if( dx < 0 ) r.x += dx;
           if( dy < 0 ) r.y += dy;

           // Move the sprite by dx,dy
           s.x += dx;
           s.y += dy;
           // TODO: Incorrect implementation..
           // collidable must be calculated from a polygon
	       return Collision.createCollidable( r ); */
	}

	public static Collidable step(Sprite s,int direction) {
		int dy = (direction & Direction.NORTH) != 0 ? -1 : 0;
			dy = (direction & Direction.SOUTH) != 0 ?  1 : dy;
		int dx = (direction & Direction.WEST)  != 0 ? -1 : 0;
			dx = (direction & Direction.EAST)  != 0 ?  1 : dx;

		return move(s,dx,dy);
	}

	public static Collidable step(Sprite s,int dx,int dy) {
		return move(s,dx,dy);
	}

    	/**
		This method calls the traveller's traversing() method for every point
		in the line from (x1,y1) to (x2,y2) except for the starting point (x1,y1).

		If while travering, the travellers's travering() method returns false,
		then this method return immediately without travelling any further. Its
		return value then will be -(n+1), where n is the number of points travelled
		by the traveller.
		<br>
		If the traveller successfully traverses the complete distance. The return
		value will be the total number of points travered.

		Note: If the return value is >=0, it signifies that the traversing
		was successfull.
		Note: This line trajectory may not be always identical to the inbuilt java line.
	*/
	public static int traverseLine(int x1,int y1,int x2,int y2,Traversable traveller) {

		int deltaX = Math.abs(x2-x1);
		int deltaY = Math.abs(y2-y1);
		int deltaMax = Math.max(deltaX,deltaY);
		double slope = (deltaX!=0)? ( (double)(y2-y1)/(x2-x1)):0;

		int xIncr = ( x2-x1>0 ) ? 1 : (x2-x1<0 ? -1:0 );
		int yIncr = ( y2-y1>0 ) ? 1 : (y2-y1<0 ? -1:0 );

		int x = x1;
		int y = y1;

		//g.drawLine(x,y,x,y);
		for(int i=0;i<deltaMax;i++) {

			int nextX = x,nextY = y;
			if( deltaX > deltaY ) {
				nextX+=xIncr;
				nextY = (int) (((nextX-x1)*slope + y1) + 0.5);
			}else {
				nextY+=yIncr;
				if( deltaX!=0 ) {
					nextX = (int) (((nextY-y1)/slope + x1) + 0.5);
				}
			}
			int dx = nextX-x;
			int dy = nextY-y;
			x+=dx;
			y+=dy;
			//----------------------------------------------------------
			// Do your stuff. Use x,y,dx,dy
			//----------------------------------------------------------
			boolean travelFurther = traveller.traversing(x,y,dx,dy,i,deltaMax);
			if( !travelFurther ) return -(i+1);

			//----------------------------------------------------------
			//g.drawLine(x,y,x,y);	// For testing purpose
		}
		return deltaMax;
	}


 }